﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

namespace LyncServerExtension.PluginModel
{
    /// <summary>
    /// Class to represent a manager for plugins.
    /// </summary>
    public sealed class PluginManager
    {
        public static readonly IEqualityComparer<IPlugin> PLUGIN_ID_EQUALITY_COMPARER = new PluginIdEqualityComparer();

        private static PluginManager instance;

        /// <summary>
        /// Gets the instance.
        /// </summary>
        public static PluginManager Instance
        {
            get
            {
                if (instance == null)
                    instance = new PluginManager();
                return instance;
            }
        }

        private PluginManager()
        {
        }

        /// <summary>
        /// Gets the plugin last version.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public IPlugin GetPluginLastVersion(string path, string id)
        {
            return GetPlugins(path, id).FirstOrDefault();
        }

        /// <summary>
        /// Gets the plugins.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="id">The id.</param>
        /// <param name="minimumVersion">The minimum version.</param>
        /// <returns></returns>
        public IPlugin[] GetPlugins(string path, string id, string minimumVersion)
        {
            return GetPlugins(path, id).TakeWhile(p => p.Version.CompareTo(minimumVersion) != -1).ToArray();
        }

        /// <summary>
        /// Gets the plugins.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public IPlugin[] GetPlugins(string path, string id)
        {
            return GetPlugins(path).Where(p => p.Id == id).OrderByDescending(p => p.Version).ToArray();
        }

        /// <summary>
        /// Gets the plugin last versions.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        public IPlugin[] GetPluginLastVersions(string path)
        {
            return GetPlugins(path).OrderByDescending(p => p.Version).Distinct(PLUGIN_ID_EQUALITY_COMPARER).ToArray();
        }

        /// <summary>
        /// Gets the plugins.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        public IPlugin[] GetPlugins(string path)
        {
            List<IPlugin> plugins = new List<IPlugin>();

            CollectPlugins(path, plugins);
            return plugins.ToArray();
        }

        private void CollectPlugins(string path, List<IPlugin> plugins)
        {
            if ((File.GetAttributes(path) & FileAttributes.Directory) != 0)
            {
                string[] files = Directory.GetFiles(path, "*.dll");

                foreach (var file in files)
                    CollectPlugins(file, plugins); 
            }
            else if (path.EndsWith(".dll"))
            {
                Assembly assembly = Assembly.LoadFile(path);
                Type[] exportedTypes = assembly.GetExportedTypes();
                IEnumerable<Type> pluginTypes = exportedTypes.Where(t => typeof(IPlugin).IsAssignableFrom(t));

                var pluginObjects = pluginTypes.Select(t => Activator.CreateInstance(t));
                foreach (var po in pluginObjects)
                    plugins.Add((IPlugin)po);
            }
        }
    }

    class PluginIdEqualityComparer : IEqualityComparer<IPlugin>
    {
        public bool Equals(IPlugin x, IPlugin y)
        {
            return x.Id == y.Id;
        }

        public int GetHashCode(IPlugin obj)
        {
            return base.GetHashCode();
        }
    }
}
